using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace Speedball2 {
  public class SpriteManager {
    public static SpriteManager Instance;

    private Dictionary<BankEnum, List<Bitmap>> _banksBitmaps;
    private Dictionary<string, List<Bitmap>> _banksBitmapsByString;
    private Dictionary<string, byte []> _banksBytes;
    private VersionEnum _version;

    private Palette _paletteMenu;
    private Palette _paletteManager;
    private Palette _paletteMatch;
    private Palette _paletteMatchBlue;
    private Palette _paletteIntro;

    private int _indexRed;
    private int _indexBlue;
    private int _indexBackground;

    public SpriteManager (VersionEnum version) {
      Instance = this;
      _banksBitmaps = new Dictionary<BankEnum, List<Bitmap>> ();
      _banksBitmapsByString = new Dictionary<string, List<Bitmap>> ();
      _banksBytes = new Dictionary<string, byte []> ();
      _version = version;

      if (_version == VersionEnum.Atari)
        InitAtari ();
      else if (_version == VersionEnum.Amiga)
        InitAmiga ();
      else
        throw new Exception ();

      string baseFolder;
      if (_version == VersionEnum.Atari)
        baseFolder = "Atari";
      else if (_version == VersionEnum.Amiga)
        baseFolder = "Amiga";
      else
        throw new Exception ();

      if (!Directory.Exists (baseFolder))
        Write (baseFolder);
    }

    private void InitAtari () {
      byte [] bytes = Game.AtariDisk;
      _indexBlue = 13;
      _indexRed = 15;
      _indexBackground = 0;

      _paletteMenu = new Palette (bytes, Game.AtariRamToDisk (0x48E), 16, Palette.PaletteLookup.ST);
      _paletteManager = new Palette (bytes, Game.AtariRamToDisk (0x46E), 16, Palette.PaletteLookup.ST);
      _paletteMatch = new Palette (bytes, Game.AtariRamToDisk (0x44E), 16, Palette.PaletteLookup.ST);
      int blue = Game.AtariDisk.ReadWord (Game.AtariRamToDisk (0x44E + _indexBlue * 2));
      bytes.WriteWord (Game.AtariRamToDisk (0x44E + _indexRed * 2), blue);
      _paletteMatchBlue = new Palette (bytes, Game.AtariRamToDisk (0x44E), 16, Palette.PaletteLookup.ST);

      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "stadium.pc1"), Game.AtariIntro, 0xEEC, 28496, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "doors.pc1"), bytes, 0x8E3C, 28495, -1, -1, null, Sprite.MasksEnum.Opaque);
      Pc1 pc1 = new Pc1 (_banksBytes [Path.Combine ("screens", "doors.pc1")], Sprite.MasksEnum.Opaque);
      _paletteIntro = pc1._palette;
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "logofont.spr"), bytes, 0xFD8C, 5632, 16, 16, _paletteIntro, Sprite.MasksEnum.Transparent);

      AddSprites (BankEnum.DiamondDat, Path.Combine ("blocks", "diamond.dat"), bytes, 0x63D6A, 3072 * 3, 96, 64, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.DmndfontSpr, Path.Combine ("fonts", "dmndfont.spr"), bytes, 0x6616A, 1152, 8, 8, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.DigitSpr, Path.Combine ("fonts", "digits.spr"), bytes, 0x63C2A, 320, 8, 8, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.StatusDat, Path.Combine ("blocks", "status.dat"), bytes, 0x6322A, 2560, 320, 16, _paletteMatch, Sprite.MasksEnum.Opaque);

      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "border.pc1"), bytes, 0x66A00, 22469, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("blocks", "blocks.dat"), bytes, 0x6C1C6, 22144, 16, 16, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "leagfont.spr"), bytes, 0x71846, 1632, 8, 8, _paletteMenu, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "bigfont.spr"), bytes, 0x71EA6, 4864, 16, 16, _paletteMenu, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "small1.spr"), bytes, 0x731A6, 928, 8, 8, _paletteMenu, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "small2.spr"), bytes, 0x73546, 1024, 8, 8, _paletteMenu, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "mngrfont.spr"), bytes, 0x73946, 576, 8, 8, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "cashfont.spr"), bytes, 0x73EE6, 320, 8, 8, _paletteManager, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "tinyfont.spr"), bytes, 0x74026, 1184, 8, 8, _paletteManager, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "lastfont.spr"), bytes, 0x744C6, 1152, 8, 8, null, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x74946, 2048, 16, 16, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x75146, 22528, 32, 32, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x7A946, 18944, 32, 32, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x7F346, 34560, 16, 16, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x87A46, 5760, 16, 16, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "reward.pc1"), bytes, 0x890C6, 20738, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "defeat.pc1"), bytes, 0x8E1C8, 16904, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "victory1.pc1"), bytes, 0x923D0, 12213, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "victory2.pc1"), bytes, 0x95386, 12230, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "victory3.pc1"), bytes, 0x9834C, 12249, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "victory4.pc1"), bytes, 0x9B326, 12212, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0x9E400, 2816, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0x9EF00, 1536, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0x9F500, 128, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "types.spr"), bytes, 0x9F580, 1024, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "types.spr"), bytes, 0x9F980, 1536, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0x9FF80, 1664, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xA0600, 1536, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xA0C00, 1024, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xA1000, 256, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "players.spr"), bytes, 0xA1100, 61440, 32, 32, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "medic.spr"), bytes, 0xB0100, 5120, 32, 32, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "medic.spr"), bytes, 0xB1500, 2560, 32, 32, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "players.spr"), bytes, 0xB1F00, 1024, 32, 32, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "medic.spr"), bytes, 0xB2300, 5120, 32, 32, _paletteMatch, Sprite.MasksEnum.Opaque);

      AddSprites (BankEnum.PlayersSprBlue, Path.Combine ("sprites", "players_blue.spr"), bytes, 0xA1100, 61440, 32, 32, _paletteMatchBlue, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.PlayersSprBlue, Path.Combine ("sprites", "medic_blue.spr"), bytes, 0xB0100, 5120, 32, 32, _paletteMatchBlue, Sprite.MasksEnum.Transparent);

      AddSprites (BankEnum.PitchDat, Path.Combine ("blocks", "pitch.dat"), bytes, 0xB3800, 4608, 32, 32, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.PitchDat, Path.Combine ("blocks", "pitch.dat"), bytes, 0xB4A00, 10240, 640, 32, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.PitchDat, Path.Combine ("blocks", "pitch.dat"), bytes, 0xB7200, 14336, 32, 32, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "unknown.spr"), bytes, 0x73B86, 864, 8, 8, _paletteManager, Sprite.MasksEnum.Opaque);
    }

    private void InitAmiga () {
      byte [] bytes = Game.AmigaDisk;
      _indexBlue = 13;
      _indexRed = 15;
      _indexBackground = 0;

      _paletteMenu = new Palette (Game.AmigaMatch, 0x16994, 32, Palette.PaletteLookup.Amiga);
      _paletteManager = new Palette (Game.AmigaMatch, 0x132, 32, Palette.PaletteLookup.Amiga);
      _paletteMatch = new Palette (Game.AmigaMatch, 0xF2, 32, Palette.PaletteLookup.Amiga);
      int blue = Game.AmigaMatch.ReadWord (0xF2 + _indexBlue * 2);
      Game.AmigaMatch.WriteWord (0xF2 + _indexRed * 2, blue);
      _paletteMatchBlue = new Palette (Game.AmigaMatch, 0xF2, 32, Palette.PaletteLookup.Amiga);
      _paletteIntro = new Palette (Game.AmigaIntro, 0xA8, 32, Palette.PaletteLookup.Amiga);

      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "stadium.ilbm"), Game.AmigaIntro, 0x49E5E, 36528, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "doors.ilbm"), Game.AmigaIntro, 0x52D0E, 33010, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "logofont.spr"), Game.AmigaIntro, 0x5AE00, 6880, 16, 16, 5, _paletteIntro, Sprite.MasksEnum.Transparent);

      AddSprites (BankEnum.DiamondDat, Path.Combine ("blocks", "diamond.dat"), Game.AmigaMatch, 0x173BE, 3840 * 3, 96, 64, 5, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.DmndfontSpr, Path.Combine ("fonts", "dmndfont.spr"), Game.AmigaMatch, 0x1AFBE, 1440, 8, 8, 5, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.DigitSpr, Path.Combine ("fonts", "digits.spr"), Game.AmigaMatch, 0x1B55E, 400, 8, 8, 5, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.StatusDat, Path.Combine ("blocks", "status.dat"), bytes, 0x68398, 2560, 320, 16, 4, _paletteMatch, Sprite.MasksEnum.Opaque);

      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "border.ilbm"), bytes, 0x68E00, 30062, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("blocks", "blocks.dat"), bytes, 0x7036E, 27680, 16, 16, 5, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "leagfont.spr"), bytes, 0x76F8E, 2040, 8, 8, 5, _paletteMenu, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "bigfont.spr"), bytes, 0x77786, 6080, 16, 16, 5, _paletteMenu, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "small1.spr"), bytes, 0x78F46, 1120, 8, 8, 5, _paletteMenu, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "small2.spr"), bytes, 0x793A6, 1120, 8, 8, 5, _paletteMenu, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "mngrfont.spr"), bytes, 0x79806, 720, 8, 8, 5, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "cashfont.spr"), bytes, 0x79AD6, 400, 8, 8, 5, _paletteManager, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "tinyfont.spr"), bytes, 0x79C66, 1440, 8, 8, 5, _paletteManager, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("fonts", "lastfont.spr"), bytes, 0x7A206, 1440, 8, 8, 5, null, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x7A7A6, 2560, 16, 16, 5, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x7B1A6, 20480, 32, 32, 5, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x801A6, 23680, 32, 32, 5, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x85E26, 34560, 16, 16, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("sprites", "buttons.spr"), bytes, 0x8E526, 7200, 16, 16, 5, _paletteManager, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "reward.ilbm"), bytes, 0x90C00, 27646, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "defeat.ilbm"), bytes, 0x97800, 22210, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "victory1.ilbm"), bytes, 0x9D000, 16376, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "victory2.ilbm"), bytes, 0xA1000, 16328, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "victory3.ilbm"), bytes, 0xA5000, 16298, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("screens", "victory4.ilbm"), bytes, 0xA9000, 16272, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xAD000, 2816, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xADB00, 1536, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xAE100, 128, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "types.spr"), bytes, 0xAE180, 1024, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "types.spr"), bytes, 0xAE580, 1536, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xAEB80, 1536, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xAF180, 1536, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xAF780, 1024, 16, 16, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.Entities, Path.Combine ("sprites", "balls.spr"), bytes, 0xAFB80, 256, 16, 16, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "players.spr"), bytes, 0xAFC80, 61440, 32, 32, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "medic.spr"), bytes, 0xBEC80, 5120, 32, 32, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "medic.spr"), bytes, 0xC0080, 2560, 32, 32, _paletteMatch, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "players.spr"), bytes, 0xC0A80, 1280, 32, 32, 5, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "players.spr"), bytes, 0xC0F80, 640, 32, 32, 5, _paletteMatch, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.PlayersSpr, Path.Combine ("sprites", "medic.spr"), bytes, 0xC1200, 6400, 32, 32, 5, _paletteMatch, Sprite.MasksEnum.Opaque);

      AddSprites (BankEnum.PlayersSprBlue, Path.Combine ("sprites", "players_blue.spr"), bytes, 0xAFC80, 61440, 32, 32, _paletteMatchBlue, Sprite.MasksEnum.Transparent);
      AddSprites (BankEnum.PlayersSprBlue, Path.Combine ("sprites", "medic_blue.spr"), bytes, 0xBEC80, 5120, 32, 32, _paletteMatchBlue, Sprite.MasksEnum.Transparent);

      AddSprites (BankEnum.PitchDat, Path.Combine ("blocks", "pitch.dat"), bytes, 0xC2C00, 59840, 16, 16, 5, _paletteMatch, Sprite.MasksEnum.Opaque);

      //AddSprites (BankEnum.ZZZ, Path.Combine ("", "pitch.map"), bytes, 0x2D64, 5760, -1, -1, null, Sprite.MasksEnum.Opaque);
      AddSprites (BankEnum.ZZZ, Path.Combine ("", "pitch.map"), Game.AmigaMatch, 0x27A, 5760, -1, -1, null, Sprite.MasksEnum.Opaque);
    }

    private Bitmap [] AddSprites (BankEnum bank, string filename, byte [] bytes, int offset, int length, int width, int height, Palette palette, Sprite.MasksEnum mask) {
      return (AddSprites (bank, filename, bytes, offset, length, width, height, 4, palette, mask));
    }

    private Bitmap [] AddSprites (BankEnum bank, string filename, byte [] bytes, int offset, int length, int width, int height, int bp, Palette palette, Sprite.MasksEnum mask) {
      if (!_banksBitmapsByString.ContainsKey (filename)) {
        if (palette != null)
          _banksBitmapsByString.Add (filename, new List<Bitmap> ());
        _banksBytes.Add (filename, new byte [0]);
      }
      byte [] bytesPart = bytes.Extract (offset, length);
      Bitmap [] bitmaps = null;
      if (palette != null) {
        bitmaps = CreateBitmaps (bytesPart, width, height, bp, palette, mask);
        _banksBitmapsByString [filename].AddRange (bitmaps);
        AddBitmaps (bank, bitmaps);
      }
      _banksBytes [filename] = _banksBytes [filename].Concat (bytesPart);
      return (bitmaps);
    }

    private void AddBitmaps (BankEnum bank, Bitmap [] bitmaps) {
      if (!_banksBitmaps.ContainsKey (bank))
        _banksBitmaps.Add (bank, new List<Bitmap> ());
      _banksBitmaps [bank].AddRange (bitmaps);
    }

    private Bitmap [] CreateBitmaps (byte [] bytes, int width, int height, int bp, Palette palette, Sprite.MasksEnum mask) {
      int nb = bytes.Length / (width * height * bp / 8);
      if (_version == VersionEnum.Atari) {
        return (Sprite.CreateBitmaps (bytes, 0, nb, width, height, 4, Sprite.ModesEnum.Interleaved, (width > 8) ? 2 : 1, palette, 0, mask));
      }
      else if (_version == VersionEnum.Amiga) {
        return (Sprite.CreateBitmaps (bytes, 0, nb, width, height, bp, Sprite.ModesEnum.Planar, -1, palette, 0, mask));
      }
      else
        throw new Exception ();
    }

    private void Write (string baseFolder) {
      foreach (KeyValuePair<string, List<Bitmap>> kvp in _banksBitmapsByString) {
        List<Bitmap> bitmaps = kvp.Value;
        if (bitmaps == null)
          continue;
        string folderAndfileName = kvp.Key;
        string folder = Path.GetDirectoryName (folderAndfileName);
        string filename = Path.GetFileName (folderAndfileName);
        string fullFolder = Path.Combine (Path.Combine (baseFolder, folder), filename.Replace ('.', '_'));
        if (!Directory.Exists (fullFolder))
          Directory.CreateDirectory (fullFolder);
        for (int i = 0; i < bitmaps.Count; i++) {
          string fullFilename = Path.Combine (fullFolder, string.Format ("{0}.png", i.ToString ().PadLeft (3, '0')));
          bitmaps [i].Save (fullFilename, ImageFormat.Png);
        }
      }

      foreach (KeyValuePair<string, byte []> kvp in _banksBytes) {
        string folderAndfileName = kvp.Key;
        byte [] bytes = kvp.Value;
        string fullFilename = Path.Combine (baseFolder, folderAndfileName);
        string fullFolder = Path.GetDirectoryName (fullFilename);
        if (!Directory.Exists (fullFolder))
          Directory.CreateDirectory (fullFolder);
        bytes.WriteToFile (fullFilename);
      }
    }

    public static Brush BlueBrush {
      get {
        return (Instance._paletteMatch.GetBrush (Instance._indexBlue));
      }
    }

    public static Brush RedBrush {
      get {
        return (Instance._paletteMatch.GetBrush (Instance._indexRed));
      }
    }

    public static Brush BackgroundBrush {
      get {
        return (Instance._paletteMatch.GetBrush (Instance._indexBackground));
      }
    }

    public static List<Bitmap> GetBank (BankEnum bank) {
      return (Instance._banksBitmaps [bank]);
    }

    public static byte [] GetBankBytes (string bank) {
      return (Instance._banksBytes [bank]);
    }

    public static Bitmap GetBitmap (BitmapEnum bank) {
      if (bank == BitmapEnum.PanelGoal)
        return (Instance._banksBitmaps [BankEnum.DiamondDat] [0]);
      else if (bank == BitmapEnum.PanelInjured)
        return (Instance._banksBitmaps [BankEnum.DiamondDat] [1]);
      else if (bank == BitmapEnum.PanelBackground)
        return (Instance._banksBitmaps [BankEnum.DiamondDat] [2]);
      else if (bank == BitmapEnum.Status)
        return (Instance._banksBitmaps [BankEnum.StatusDat] [0]);
      else
        throw new Exception ();
    }

    public static int WallLeftIndex {
      get {
        if (Version == VersionEnum.Atari)
          return (10);
        else if (Version == VersionEnum.Amiga)
          throw new Exception ();
        else
          throw new Exception ();
      }
    }

    public static int WallRightIndex {
      get {
        if (Version == VersionEnum.Atari)
          return (WallLeftIndex + 14);
        else if (Version == VersionEnum.Amiga)
          throw new Exception ();
        else
          throw new Exception ();
      }
    }

    public static VersionEnum Version {
      get {
        return (Instance._version);
      }
    }
  }

  public enum BankEnum {
    ZZZ,
    PitchDat,
    DiamondDat,
    DmndfontSpr,
    StatusDat,
    DigitSpr,
    Entities,
    PlayersSpr,
    PlayersSprBlue,
    //BigBalls,
  }

  public enum BitmapEnum {
    PanelGoal,
    PanelInjured,
    PanelBackground,
    Status,
  }

  public enum VersionEnum {
    Atari,
    Amiga
  }
}
